package com.css.crm.listener;

import java.util.Arrays;

import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlInputSecret;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.apache.log4j.MDC;

import com.css.crm.vo.AbstractUser;

/**
 * 
 * @author steven.lam.t.f
 * 
 */
public class JSFPerformanceLogger implements PhaseListener{
	private static final long serialVersionUID = 2799464795082930073L;
	private static final Logger logger = Logger.getLogger("PerformanceLog");
	private static final String[] phaseIdStrings = new String[]{PhaseId.RESTORE_VIEW.toString(), PhaseId.APPLY_REQUEST_VALUES.toString(), PhaseId.PROCESS_VALIDATIONS.toString(), PhaseId.UPDATE_MODEL_VALUES.toString(), PhaseId.INVOKE_APPLICATION.toString(), PhaseId.RENDER_RESPONSE.toString()};

	public void beforePhase(final PhaseEvent event){
		try{
			((HttpSession)event.getFacesContext().getExternalContext().getSession(true)).setAttribute(event.getPhaseId().toString(), new Long(System.currentTimeMillis()));
		}catch(final Exception e){
			logger.error("beforePhase", e);
		}
	}
	public void afterPhase(final PhaseEvent event){
		try{
			final String phaseId = event.getPhaseId().toString();
			final Object startTime = ((HttpSession)event.getFacesContext().getExternalContext().getSession(true)).getAttribute(phaseId);
			if(event.getFacesContext().getViewRoot() != null){
				MDC.put("viewId", event.getFacesContext().getViewRoot().getViewId());
			}
			if(event.getPhaseId() == PhaseId.RESTORE_VIEW){
				final AbstractUser user = (AbstractUser)event.getFacesContext().getELContext().getELResolver().getValue(event.getFacesContext().getELContext(), null, "user");
				if(user != null && user.getId() != null){
					MDC.put("userLdapId", user.getId());
				}
				final StringBuilder sb = new StringBuilder(1024);
				final String[] keys = event.getFacesContext().getExternalContext().getRequestParameterValuesMap().keySet().toArray(new String[0]);
				Arrays.sort(keys);
				for(final String key : keys){
					final String[] values = event.getFacesContext().getExternalContext().getRequestParameterValuesMap().get(key);
					if(values == null){
						sb.append(key).append("=, ");
					}else{
						for(final String value : values){
							try{
								final UIComponent c = event.getFacesContext().getViewRoot().findComponent(key);
								if(c == null || HtmlInputSecret.class.isAssignableFrom(c.getClass()) == false){
									sb.append(key).append("=").append(value).append(", ");
								}else{
									sb.append(key).append("=(hidden), ");
								}
							}catch(final IllegalArgumentException e){
								sb.append(key).append("=").append(value).append(", ");
							}
						}
					}
				}
				if(sb.length() > 2){
					sb.setLength(sb.length() - 2);
				}
				logger.info("request parameters: " + (sb.length() == 0 ? "(none)" : sb.toString()));
			}
			logger.info(phaseId + " spent " + (startTime == null || Long.class.isAssignableFrom(startTime.getClass()) == false ? "??" : System.currentTimeMillis() - ((Long)startTime).longValue()) + " ms.");
			if(event.getPhaseId() == PhaseId.RENDER_RESPONSE){
				final Object restoreViewTime = ((HttpSession)event.getFacesContext().getExternalContext().getSession(true)).getAttribute(PhaseId.RESTORE_VIEW.toString());
				logger.info("Overall spent " + (restoreViewTime == null || Long.class.isAssignableFrom(restoreViewTime.getClass()) == false ? "??" : System.currentTimeMillis() - ((Long)restoreViewTime).longValue()) + " ms.");
			}
		}catch(final Exception e){
			logger.error("afterPhase", e);
		}
	}
	public PhaseId getPhaseId(){
		return PhaseId.ANY_PHASE;
	}
	public static boolean isPhaseIdString(final String p){
		for(final String s : phaseIdStrings){
			if(s.equals(p)){
				return true;
			}
		}
		return false;
	}
}
